import fs from 'fs'
import { tools } from './tools/tools';

export namespace res_format {

    const SKIN_PATH = 'eui_skins';
    const SKIN_PATH_WEB = 'eui_skins_web';

    type FOLDER_REPORT_TYPE = {
        fullFileNum: number;
        fullIncludeFileNum: number;
        fullFileCoverdRatio: number
        fileNum: number;
        includeFileNum: number;
        fileCoverdRatio: number;
        folderNum: number;
        childFolder?: { [name: string]: FOLDER_REPORT_TYPE };
        unInfludeFiles: string[];
    };

    /**
     * 基础目录结构
     */
    class Folder {
        constructor(name: string, parent?: Folder) {
            this.name = name;
            this.parent = parent;
            parent && parent.PushChildFolder(this);
        }

        public PushFile(name: string): void {
            if (!this.files) {
                this.files = [];
            }
            if (this.files.indexOf(name) >= 0) {
                console.error('Duplacate File !!!')
            } else {
                this.files.push(name);
                ++this.FullFileNum;
                let parent = this.parent;
                while (parent) {
                    ++parent.FullFileNum;
                    parent = parent.parent;
                }
            }
        }

        public GetFolderByName(name: string): Folder | null {
            if (!this.folders) {
                return null;
            }
            return this.folders[name];
        }

        private PushChildFolder(folder: Folder): void {
            if (!this.folders) {
                this.folders = {};
            }
            if (!!this.folders[folder.name]) {
                console.error('Duplacate Folder !!!')
                return;
            }
            this.folders[folder.name] = folder;
            ++this.foldersNum;
        }

        public get Name(): string {
            return this.name;
        }

        /**
         * 当前文件夹所在的完整路径(从resource开始)
         */
        public get FullPath(): string {
            if (this.fullPath != undefined) {
                return this.fullPath;
            }
            this.fullPath = this.name;
            this.parent && (this.fullPath = this.parent.fullPath + '/' + this.fullPath)
            return this.fullPath;
        }

        /**
         * 获取自己的父节点（根节点没有父节点）
         */
        public get Parent(): Folder | undefined {
            return this.parent;
        }

        /**
         * 当前文件夹下是否存在子文件夹
         */
        public get HasFolders(): boolean {
            return !!this.folders;
        }

        /**
         * 当前文件夹下面(包括子文件夹)的文件夹
         */
        public get Folders(): { [folderName: string]: Folder } | null {
            return this.folders;
        }

        public get FoldersName(): string[] {
            return !!this.folders && Object.keys(this.folders) || [];
        }

        /**
         * 当前文件夹下面(包括子文件夹)的文件数量
         */
        public get FullFilesNum(): number {
            return this.FullFileNum;
        }

        /**
         * 当前文件夹下面的子文件夹数量
         */
        public get FoldersNum(): number {
            return this.foldersNum;
        }

        /**
         * 当前文件夹下面的文件数量(子文件夹内的文件不包括在内)
         */
        public get FilesNum(): number {
            return this.files ? this.files.length : 0;
        }

        /**
         * 当前文件夹下面的所有文件(子文件夹内的文件不包括在内)
         */
        public get Files(): string[] | null {
            return this.files;
        }

        protected name: string;
        protected folders: { [folderName: string]: Folder } | null = null;
        protected files: string[] | null = null;
        protected parent: Folder | undefined;

        private fullPath!: string;

        private foldersNum: number = 0;
        private FullFileNum: number = 0;
    }

    /**
     * 带有资源标记的目录结构
     */
    export class ResFolder extends Folder {
        constructor(ame: string, parent?: ResFolder) {
            super(ame, parent);

            this.checkedFiles = [];
        }

        public PushFile(name: string): void {
            super.PushFile(name);
        }

        /**
         * 会把自己以及自己的子节点的状态全部重置掉
         */
        public Reset(): void {
            this.checkedFiles.splice(0);
            this.coverAllFiles = false;
            this.hasCheckedFile = false;
            this.fullCheckedFilesNum = 0;
            if (!!this.folders) {
                for (let folder in this.folders) {
                    this.folders[folder].Reset();
                }
            }
        }

        /**
         * 调用的时候需要先将文件结构构建
         * @param file 文件名字(不需要路径);
         */
        public CheckFile(file: string): void {
            if (!Number.isNaN(this.files?.indexOf(file))) {
                this.checkedFiles.indexOf(file) < 0 && this.checkedFiles.indexOf(file) < 0 && this.checkedFiles.push(file);
                this.hasCheckedFile = true;
                ++this.fullCheckedFilesNum;

                let parent = this.parent;
                while (parent) {
                    !parent.hasCheckedFile && (parent.hasCheckedFile = true);
                    ++parent.fullCheckedFilesNum;
                    parent = parent.parent;
                }
            } else {
                console.error(`file ${file} does not exist in this folder`);
            }

            if (this.files?.length == this.checkedFiles.length) { //当前文件夹内包含全部文件时的处理
                this.parent && this.parent.CheckFolder();
            }
        }

        public GetFolderByName(name: string): ResFolder | null {
            return super.GetFolderByName(name) as ResFolder;
        }

        private CheckFolder(): void {
            let coverAllFolders: boolean = true;
            if (!!this.folders) {
                let folders = Object.keys(this.folders);
                for (let childFolder of folders) {
                    if (!this.folders[childFolder].CoverFiles) {
                        coverAllFolders = false;
                        break;
                    }
                }
            }
            if (this.CoverFiles && coverAllFolders) {
                this.coverAllFiles = true;
                this.parent && this.parent.CheckFolder(); //往上滚
            }
        }

        /**
         * 包含这个文件夹下面除文件夹的所有文件
         */
        public get CoverFiles(): boolean {
            if (!this.files || 0 == this.files.length) {
                return true;
            }
            return this.files.length <= this.checkedFiles.length; //等于才对，这里防止异常，包括的也算进去
        }

        /**
         * 当前文件夹下面包括子文件夹的文件全部包括
         */
        public get CoverAllFiles(): boolean {
            return this.coverAllFiles;
        }

        /**
         * 当前文件夹下面检查存在的文件数量
         */
        public get CheckedFiles(): string[] {
            return this.checkedFiles;
        }

        /**
         * 当前文件夹包括子文件夹检查存在的文件数量
         */
        public get FullCheckedFilesNum(): number {
            return this.fullCheckedFilesNum;
        }

        public get CheckedFilesNum(): number {
            return this.checkedFiles.length;
        }

        /**
         * 里面有要加入的文件
         */
        public get HasCheckedFile(): boolean {
            return this.hasCheckedFile;
        }

        /**
         * 生成当前文件夹下所有未曾被检查的文件的集合(不包括子文件夹下面的文件)
         * @param fullPath - 是否包含完整路径(从resource开始)
         */
        public GenUnIncludeFiles(fullPath?: boolean): string[] {
            let result: string[] = [];
            if (!this.files) {
                return result;
            }
            for (let file of this.files) {
                this.checkedFiles.indexOf(file) < 0 && result.indexOf(file) < 0 && result.push(fullPath ? `${this.FullPath}/${file}` : file);
            }
            return result;
        }

        public GenIncludfiles(fullPath?: boolean): string[] {
            let result: string[] = [];
            if (!this.files) {
                return result;
            }
            for (let file of this.checkedFiles) {
                result.indexOf(file) < 0 && result.push(fullPath ? `${this.FullPath}/${file}` : file);
            }
            return result;
        }

        protected parent: ResFolder | undefined;
        protected folders: { [folderName: string]: ResFolder } | null = null;

        private checkedFiles: string[];
        private fullCheckedFilesNum: number = 0;
        private coverAllFiles: boolean = false;
        private hasCheckedFile: boolean = false;
    }

    export class ResCheck {
        constructor(entry: string) {
            this.entry = entry;
        }

        public BuildTree(): ResCheck {
            this.root = this.DF(new ResFolder('resource'));
            return this;
        }

        public get CurRoot(): ResFolder {
            return this.root;
        }

        /**
         *
         * @param files
         * @param isGroup - 为false时会将皮肤文件全部包含进去
         * @returns
         */
        public CheckFiles(files: string[], isGroup: boolean): tools.GROUP_CFG_TYPE {
            let result: tools.GROUP_CFG_TYPE = { include: [], exclude: [] };
            if (!files || files.length < 1) {
                return result;
            }
            this.root.Reset();
            for (let file of files) {
                this.CheckFile(file);
            }
            this.GenResult(this.root, result, isGroup);
            this.RootChoper(result);
            return result;
        }

        private GenResult(root: ResFolder | null, result: tools.GROUP_CFG_TYPE, isGroup: boolean) {
            if (!root) {
                return;
            }

            if (root.CoverAllFiles) {
                result.include.push(`${root.FullPath}/**`);
                return;
            }
            if (root.HasCheckedFile) { //当前文件夹有检查过的文件
                if (root.FullCheckedFilesNum / root.FullFilesNum > 0.5) { //考虑全包
                    let excludeCfgs: string[] = []; //排除项扔到这里，后面通过之后一次性归到结果里面
                    if (this.CheckFolder(root, excludeCfgs)) { //当前文件夹下面的文件都能加进去
                        result.include.push(`${root.FullPath}/**`);
                        result.exclude.push(...excludeCfgs);
                        // this.GenFolderReport(root);
                        if (isGroup) {
                            result.exclude.push(...root.GenUnIncludeFiles(true));
                        } else {
                            !this.CheckIsSkinFolder(root.Name) && result.exclude.push(...root.GenUnIncludeFiles(true));
                        }
                    } else {
                        this.DoFaiOperation(root, result, isGroup);
                    }
                } else { //包不下去
                    this.DoFaiOperation(root, result, isGroup);
                }
            }
        }

        public GenFolderReport(): FOLDER_REPORT_TYPE;
        public GenFolderReport(root: ResFolder): FOLDER_REPORT_TYPE | null
        public GenFolderReport(root: ResFolder = this.root): FOLDER_REPORT_TYPE | null {
            if (!root) {
                return null;
            }


            let result: FOLDER_REPORT_TYPE = {
                fullFileNum: root.FullFilesNum,
                fullIncludeFileNum: root.FullCheckedFilesNum,
                fullFileCoverdRatio: root.FullCheckedFilesNum / root.FullFilesNum,
                fileNum: root.FilesNum,
                includeFileNum: root.CheckedFilesNum,
                fileCoverdRatio: root.FoldersNum == 0 ? 0 : root.CheckedFilesNum / root.FilesNum,
                folderNum: root.FoldersNum,
                unInfludeFiles: root.GenUnIncludeFiles(),
            }

            if (root.HasFolders) {
                let folder: ResFolder;
                let childFolder = result.childFolder = result.childFolder || {};
                for (let folderName of root.FoldersName) {
                    folder = root.GetFolderByName(folderName)!;
                    if (folder.HasCheckedFile) {
                        childFolder[folderName] = this.GenFolderReport(folder)!;
                    }
                }
            }

            return result
        }

        private CheckIsSkinFolder(folderName: string): boolean {
            return SKIN_PATH_WEB == folderName || SKIN_PATH == folderName;
        }

        private CheckFolder(root: ResFolder, excludeCfgs: string[]): boolean {
            let tmpFolder: ResFolder;
            for (let folder of root.FoldersName) {
                tmpFolder = root.GetFolderByName(folder)!;
                if (tmpFolder.HasCheckedFile) {
                    if (tmpFolder.FullCheckedFilesNum / tmpFolder.FullFilesNum > 0.5) { //排除排除能继续用
                        excludeCfgs.push(...tmpFolder.GenUnIncludeFiles(true));
                        if (!this.CheckFolder(tmpFolder, excludeCfgs)) {
                            return false;
                        }
                    } else { //没法过了
                        return false
                    }
                } else {//整个文件夹都不包
                    excludeCfgs.push(`${tmpFolder.FullPath}/**`);
                }
            }
            return true;
        }

        private DoFaiOperation(folder: ResFolder, result: tools.GROUP_CFG_TYPE, isGroup: boolean) {
            if (!folder) {
                return;
            }

            if (folder.CheckedFilesNum / folder.FilesNum > 0.5) {
                result.include.push(`${folder.FullPath}/**.*`);
                result.exclude.push(...folder.GenUnIncludeFiles(true));
            } else {
                result.include.push(...folder.GenIncludfiles(true));
            }
            for (let folderName of folder.FoldersName) {
                this.GenResult(folder.GetFolderByName(folderName)!, result, isGroup);
            }
        }

        private CheckFile(file: string): void {
            let paths: string[] = file.split('/');
            let curRoot: ResFolder | null = this.root;
            for (let index = 0, L = paths.length, actLength = L - 1; index < L; ++index) {
                if (!curRoot) {
                    break;
                }
                if (index == actLength) {
                    curRoot.CheckFile(paths[index]);
                    break;
                }
                curRoot = curRoot.GetFolderByName(paths[index]);
            }
        }

        private DF(root: ResFolder): ResFolder {
            let rootPath = `${this.entry}/${root.FullPath}`;
            let files = fs.readdirSync(rootPath);
            for (let file of files) {
                if (fs.statSync(`${rootPath}/${file}`).isDirectory()) {
                    this.DF(new ResFolder(file, root));
                } else {
                    root.PushFile(file);
                }
            }
            return root;
        }

        private RootChoper(res: tools.GROUP_CFG_TYPE): void {
            if (!res) {
                return;
            }
            let root = this.root.FullPath + '/';
            for (let i = 0, L = res.include.length; i < L; ++i) {
                res.include[i] = res.include[i].slice(root.length);
            }

            for (let i = 0, L = res.exclude.length; i < L; ++i) {
                res.exclude[i] = res.exclude[i].slice(root.length);
            }
        }

        private entry: string;
        private root!: ResFolder;
    }
}